/**
 * @brief 内存池小池塘
 * @version 1.0
 * @author 宋炜
 */
#pragma once

#include <cstdlib>
#include <type_traits>
#include <unordered_set>

#include <memory>

namespace wheels{

template< typename type_t >
class pond{
public:
    struct stDelter{
        void operator()( type_t* p ){
            //MSG_1( "delete []p %p", TGREEN , p );
            delete []p;
        }
    };

    using drop_t = std::shared_ptr< type_t >;
public:
    /**
     * @brief 构造函数
     * @param size 内存块大小
     * @param count 内存池大小
     */
    pond(size_t size, size_t count = 1024 ) :m_count__( count ), m_size__( size ){
        for( size_t i = 0; i < count; ++i ){
            m_unused__.insert( drop_t( new type_t[ size ], stDelter() ) );
        }
    }
    ~pond(){        // 先重置已使用的内存块        
        for( auto p : m_used__ ){            
            p.reset();        
        }        
        // 再重置未使用的内存块        
        for( auto p : m_unused__ ){            
            p.reset();        
        }        
        // 清空容器        
        m_used__.clear();        
        m_unused__.clear();    
    }
    /**
     * @brief 调整内存池大小。调整内存池的大小不是调整分页的大小，而是条水滴的数量
     * @param count 新的内存池大小
     * @return true 调整成功
     * @return false 调整失败
     */
    bool reCount( size_t count )
    {
        if( count == m_count__ ) return false;
        if( count < m_count__ ){
            size_t sub = m_count__ - count;
            if( sub > m_unused__.size() ){ // 未使用的内存块数量不足，则清空没有使用的内存块
                size_t unuse = m_unused__.size();
                m_unused__.clear();
                m_count__ -= unuse;
            }
            for( size_t i = 0; i < sub && !m_unused__.empty(); ++i ){
                m_unused__.erase( m_unused__.begin() );
            }
        }else{
            try{
                for( size_t i = m_count__; i < count; ++i ){
                    m_unused__.insert( drop_t( new type_t[ m_size__ ], stDelter() ) );
                }
            }catch( std::bad_alloc& ){
                return false;
            }
        }
        m_count__ = count;
        return true;
    }
    /**
     * @brief 分配内存。分配内存块时，会从未使用的内存块列表中取出一个内存块，
     * 如果未使用的内存块列表为空，则会调整内存池大小。
     * @return std::shared_ptr< type > 分配的内存块
     */
    std::shared_ptr< type_t > malloc(){
        if( m_unused__.empty() ){
            reCount( m_count__ * 2 );
        }

        {
            auto p = *m_unused__.begin();
            m_unused__.erase( m_unused__.begin() );
            m_used__.insert( p );
            return p;
        }
        return nullptr;
    }
    /**
     * @brief 释放内存。将内存返回到没有使用的列表中
     * @param p 要释放的内存块
     */
    void free( drop_t p ){
        auto it = m_used__.find( p );
        if( it == m_used__.end() ){
            return;
        }
        m_used__.erase( it );
        m_unused__.insert( p );
    }

    /**
     * @brief 获取已使用的内存块数量
     * @return size_t 已使用的内存块数量
     */
    size_t usedCount() const{
        return m_used__.size();
    }
    /**
     * @brief 获取未使用的内存块数量
     * @return size_t 未使用的内存块数量
     */
    size_t unusedCount() const{
        return m_unused__.size();
    }
private:
    std::unordered_set< drop_t >         m_unused__;     // 未使用的内存块
    std::unordered_set< drop_t >         m_used__;       // 已使用的内存块

    size_t                      m_count__ ;     // 内存池大小
    size_t                      m_size__;       // 内存块大小
};
}